﻿using System;
using System.IO;

namespace UtilZ.Dotnet.Ex.Audio
{
    unsafe public partial class WaveHelper
    {
        #region 获取wav文件头

        /// <summary>
        /// 获取wav文件头信息,返回null表示不支持的格式
        /// </summary>
        /// <param name="wavFilePath">wav文件路径</param>
        /// <returns>wav文件头信息</returns>
        public static WaveInfo GetWavHead(string wavFilePath)
        {
            if (string.IsNullOrWhiteSpace(wavFilePath))
            {
                throw new ArgumentNullException(nameof(wavFilePath), "wave文件路径不能为空.");
            }

            if (File.Exists(wavFilePath))
            {
                using (var wavStream = new FileStream(wavFilePath, FileMode.Open, FileAccess.Read))
                {
                    return PrimitiveGetWavHead(wavStream, wavFilePath);
                }
            }
            else
            {
                throw new FileNotFoundException("wave文件不存在", wavFilePath);
            }
        }

        /// <summary>
        /// 获取wav文件头信息,返回null表示不支持的格式
        /// </summary>
        /// <param name="wavStream">wave数据流</param>
        /// <returns>wav文件头信息</returns>
        public static WaveInfo GetWavHead(Stream wavStream)
        {
            if (wavStream == null)
            {
                throw new ArgumentNullException(nameof(wavStream));
            }

            if (!wavStream.CanRead)
            {
                throw new InvalidOperationException("数据流不可读.");
            }

            return PrimitiveGetWavHead(wavStream, null);
        }

        /// <summary>
        /// 获取wav文件头信息
        /// </summary>
        /// <param name="wavStream">wave数据流</param>
        /// <param name="wavFilePath">wav文件路径</param>
        /// <returns>wav文件头信息</returns>
        private static WaveInfo PrimitiveGetWavHead(Stream wavStream, string wavFilePath)
        {
            var reader = new BinaryReader(wavStream);
            char ch1, ch2, ch3, ch4;

            //RIFF，资源交换文件标志,4byte
            ch1 = reader.ReadChar();
            ch2 = reader.ReadChar();
            ch3 = reader.ReadChar();
            ch4 = reader.ReadChar();
            if (ch1 != 'R' && ch1 != 'r' ||
                ch2 != 'I' && ch2 != 'i' ||
                ch3 != 'F' && ch3 != 'f' ||
                ch4 != 'F' && ch4 != 'f')
            {
                return null;
            }

            uint length = reader.ReadUInt32();//从RIFF下一个地址开始到文件尾的总字节数,4byte

            //WAVE，代表wav文件格式,4byte
            ch1 = reader.ReadChar();
            ch2 = reader.ReadChar();
            ch3 = reader.ReadChar();
            ch4 = reader.ReadChar();
            if (ch1 != 'W' && ch1 != 'w' ||
                ch2 != 'A' && ch2 != 'a' ||
                ch3 != 'V' && ch3 != 'v' ||
                ch4 != 'E' && ch4 != 'e')
            {
                return null;
            }

            //fmt ，波形格式标志（fmt ）,4byte
            ch1 = reader.ReadChar();
            ch2 = reader.ReadChar();
            ch3 = reader.ReadChar();
            ch4 = reader.ReadChar();
            if (ch1 != 'F' && ch1 != 'f' ||
                ch2 != 'M' && ch2 != 'm' ||
                ch3 != 'T' && ch3 != 't')
            {
                return null;
            }

            //格式块的大小
            int fmtSize = reader.ReadInt32();

            //为1时表示线性PCM编码，大于1时表示有压缩的编码。这里是0001H,2byte
            WaveEncoding encoding = (WaveEncoding)reader.ReadInt16();

            short chs, sampleBits, blockAlign;
            int sampleRate, trunkSize, fact;
            uint waveDataLength;
            bool readResult;
            switch (encoding)
            {
                case WaveEncoding.Pcm:
                    fact = 0;
                    readResult = ReadPcmWavHead(reader, out chs, out sampleRate, out trunkSize, out blockAlign, out sampleBits, out waveDataLength);
                    break;
                case WaveEncoding.IeeeFloat:
                    readResult = ReadIeeeFloatHeadWavHead(reader, fmtSize, length, out chs, out sampleRate, out trunkSize, out blockAlign, out sampleBits, out fact, out waveDataLength);
                    break;
                case WaveEncoding.ALaw:
                case WaveEncoding.MuLaw:
                    readResult = ReadALawAndMuLawHeadWavHead(reader, out chs, out sampleRate, out trunkSize, out blockAlign, out sampleBits, out fact, out waveDataLength);
                    break;
                case WaveEncoding.Adpcm:
                    readResult = ReadADPCMHeadWavHead(reader, out chs, out sampleRate, out trunkSize, out blockAlign, out sampleBits, out fact, out waveDataLength);
                    break;
                case WaveEncoding.IbmCvsd:
                    throw new NotImplementedException($"{WaveEncoding.IbmCvsd}未实现的头读取.");
                default:
                    return null;
            }

            if (readResult)
            {
                return new WaveInfo(encoding, sampleRate, trunkSize, blockAlign, chs, sampleBits, fact, waveDataLength)
                {
                    FilePath = wavFilePath
                };
            }
            else
            {
                return null;
            }
        }

        private static bool ReadADPCMHeadWavHead(BinaryReader reader, out short chs, out int sampleRate, out int trunkSize, out short blockAlign, out short sampleBits, out int fact, out uint waveDataLength)
        {
            //[声道数] 1为单声道，2为双声道,2byte
            chs = reader.ReadInt16();

            //[采样频率],4byte
            sampleRate = reader.ReadInt32();

            //[播放速度(传输速度)] 数据传输率,含义:播波形数据传输速率（每秒平均字节数,每秒所需播放字节数）
            trunkSize = reader.ReadInt32();

            //块对齐
            blockAlign = reader.ReadInt16();

            //[采样位数] 样本数据位数，0010H即16，一个量化样本占2byte(8位/16位/32位)
            sampleBits = reader.ReadInt16();

            reader.ReadInt16();
            var samplesPerBlock = reader.ReadInt16();
            var numCoeff = reader.ReadInt16();
            short[] coefficients = new short[14];
            for (int i = 0; i < coefficients.Length; i++)
            {
                coefficients[i] = reader.ReadInt16();
            }
            //reader.BaseStream.Position += 28;

            char ch1, ch2, ch3, ch4;
            waveDataLength = 0;
            fact = 0;

            //fact标志,4byte
            ch1 = reader.ReadChar();
            ch2 = reader.ReadChar();
            ch3 = reader.ReadChar();
            ch4 = reader.ReadChar();
            if (ch1 != 'F' && ch1 != 'f' ||
                ch2 != 'A' && ch2 != 'a' ||
                ch3 != 'C' && ch3 != 'c' ||
                ch4 != 'T' && ch4 != 't')
            {
                return false;
            }


            var temp1 = reader.ReadInt32();
            fact = reader.ReadInt32();

            //data标志,4byte
            ch1 = reader.ReadChar();
            ch2 = reader.ReadChar();
            ch3 = reader.ReadChar();
            ch4 = reader.ReadChar();
            if (ch1 != 'D' && ch1 != 'd' ||
                ch2 != 'A' && ch2 != 'a' ||
                ch3 != 'T' && ch3 != 't' ||
                ch4 != 'A' && ch4 != 'a')
            {
                return false;
            }

            //Wav文件实际音频数据所占的大小，这里是001437C8H即1325000，再加上2CH就正好是1325044，整个文件的大小,4byte               
            waveDataLength = reader.ReadUInt32();
            return true;
        }

        private static bool ReadALawAndMuLawHeadWavHead(BinaryReader reader, out short chs, out int sampleRate, out int trunkSize, out short blockAlign, out short sampleBits, out int fact, out uint waveDataLength)
        {
            //[声道数] 1为单声道，2为双声道,2byte
            chs = reader.ReadInt16();

            //[采样频率],4byte
            sampleRate = reader.ReadInt32();

            //[播放速度(传输速度)] 数据传输率,含义:播波形数据传输速率（每秒平均字节数,每秒所需播放字节数）
            trunkSize = reader.ReadInt32();

            //对齐块,2byte
            blockAlign = reader.ReadInt16();

            //[采样位数] 样本数据位数，0010H即16，一个量化样本占2byte(8位/16位/32位)
            sampleBits = reader.ReadInt16();
            reader.ReadInt16();


            //fact
            char ch1, ch2, ch3, ch4;
            waveDataLength = 0;
            fact = 0;

            //fact标志,4byte
            ch1 = reader.ReadChar();
            ch2 = reader.ReadChar();
            ch3 = reader.ReadChar();
            ch4 = reader.ReadChar();
            if (ch1 != 'F' && ch1 != 'f' ||
                ch2 != 'A' && ch2 != 'a' ||
                ch3 != 'C' && ch3 != 'c' ||
                ch4 != 'T' && ch4 != 't')
            {
                return false;
            }

            reader.ReadInt32();
            fact = reader.ReadInt32();

            //data标志,4byte
            ch1 = reader.ReadChar();
            ch2 = reader.ReadChar();
            ch3 = reader.ReadChar();
            ch4 = reader.ReadChar();
            if (ch1 != 'D' && ch1 != 'd' ||
                ch2 != 'A' && ch2 != 'a' ||
                ch3 != 'T' && ch3 != 't' ||
                ch4 != 'A' && ch4 != 'a')
            {
                return false;
            }

            waveDataLength = reader.ReadUInt32();
            return true;
        }

        private static bool ReadIeeeFloatHeadWavHead(BinaryReader reader, int fmtSize, uint length, out short chs, out int sampleRate, out int trunkSize, out short blockAlign, out short sampleBits, out int fact, out uint waveDataLength)
        {
            //[声道数] 1为单声道，2为双声道,2byte
            chs = reader.ReadInt16();

            //[采样频率],4byte
            sampleRate = reader.ReadInt32();

            //[播放速度(传输速度)] 数据传输率,含义:播波形数据传输速率（每秒平均字节数,每秒所需播放字节数）,Bite率;[数据类型:long] 注: Bite率=采样频率*声道数*采样样本位数/8
            //数据传输速率，计算公式为：采样率×声道数×采样位数/8
            trunkSize = reader.ReadInt32();

            //块对齐=声道数*每次采样得到的样本位数/8，0002H，也就是2=1*16/8,2byte
            blockAlign = reader.ReadInt16();

            //[采样位数] 样本数据位数，0010H即16，一个量化样本占2byte(8位/16位/32位)
            sampleBits = reader.ReadInt16();

            waveDataLength = 0;
            fact = 0;
            char ch1, ch2, ch3, ch4;
            //注fact在pcm数据头中可选项,通常是不需要的
            if (fmtSize < 16)
            {
                //非wav格式
                return false;
            }
            else if (fmtSize == 16)
            {
                //没有fact

                //data标志,4byte
                ch1 = reader.ReadChar();
                ch2 = reader.ReadChar();
                ch3 = reader.ReadChar();
                ch4 = reader.ReadChar();
                if (ch1 != 'D' && ch1 != 'd' ||
                    ch2 != 'A' && ch2 != 'a' ||
                    ch3 != 'T' && ch3 != 't' ||
                    ch4 != 'A' && ch4 != 'a')
                {
                    return false;
                }

                //Wav文件实际音频数据所占的大小，这里是001437C8H即1325000，再加上2CH就正好是1325044，整个文件的大小,4byte               
                waveDataLength = reader.ReadUInt32();
            }
            else
            {
                //有fact
                reader.ReadInt16();

                //fact标志,4byte
                ch1 = reader.ReadChar();
                ch2 = reader.ReadChar();
                ch3 = reader.ReadChar();
                ch4 = reader.ReadChar();
                if (ch1 != 'F' && ch1 != 'f' ||
                    ch2 != 'A' && ch2 != 'a' ||
                    ch3 != 'C' && ch3 != 'c' ||
                    ch4 != 'T' && ch4 != 't')
                {
                    return false;
                }


                reader.ReadInt32();
                fact = reader.ReadInt32();

                //data标志,4byte
                ch1 = reader.ReadChar();
                ch2 = reader.ReadChar();
                ch3 = reader.ReadChar();
                ch4 = reader.ReadChar();
                if (ch1 != 'D' && ch1 != 'd' ||
                    ch2 != 'A' && ch2 != 'a' ||
                    ch3 != 'T' && ch3 != 't' ||
                    ch4 != 'A' && ch4 != 'a')
                {
                    return false;
                }

                //Wav文件实际音频数据所占的大小，这里是001437C8H即1325000，再加上2CH就正好是1325044，整个文件的大小,4byte               
                waveDataLength = reader.ReadUInt32();
            }

            return true;
        }

        private static bool ReadPcmWavHead(BinaryReader reader, out short chs, out int sampleRate, out int trunkSize, out short blockAlign, out short sampleBits, out uint waveDataLength)
        {
            //[声道数] 1为单声道，2为双声道,2byte
            chs = reader.ReadInt16();

            //[采样频率],4byte
            sampleRate = reader.ReadInt32();

            //[播放速度(传输速度)] 数据传输率,含义:播波形数据传输速率（每秒平均字节数,每秒所需播放字节数）,Bit率;[数据类型:long] 注: Bite率=采样频率*声道数*采样样本位数/8
            trunkSize = reader.ReadInt32();

            //块对齐=声道数*每次采样得到的样本位数/8，0002H，也就是2=1*16/8,2byte
            blockAlign = reader.ReadInt16();

            //[采样位数] 样本数据位数，0010H即16，一个量化样本占2byte,2byte ????
            sampleBits = reader.ReadInt16();

            char ch1, ch2, ch3, ch4;
            waveDataLength = 0;

            //data标志,4byte
            ch1 = reader.ReadChar();
            ch2 = reader.ReadChar();
            ch3 = reader.ReadChar();
            ch4 = reader.ReadChar();
            if (ch1 != 'D' && ch1 != 'd' ||
                ch2 != 'A' && ch2 != 'a' ||
                ch3 != 'T' && ch3 != 't' ||
                ch4 != 'A' && ch4 != 'a')
            {
                return false;
            }

            //Wav文件实际音频数据所占的大小，这里是001437C8H即1325000，再加上2CH就正好是1325044，整个文件的大小,4byte               
            waveDataLength = reader.ReadUInt32();
            return true;
        }

        #endregion
    }
}
